package cn.echoparrot.domain.service;

import cn.echoparrot.domain.entity.Note;
import cn.echoparrot.domain.entity.Notebook;
import cn.echoparrot.repository.neo4j.NoteMapper;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.filter.Filter;
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
import org.springframework.ai.vectorstore.neo4j.Neo4jVectorStore;
import org.springframework.util.Assert;

import java.util.List;
import java.util.Map;

/**
 * 使用 Spring Data Neo4j  与 spring ai 里的 Neo4jVectorStore。
 * 可以大量简化自己的时间，不需要写那么多的Cypher语句，减少调试的时间。
 *
 * 可以将这两部分的内容，在Service层面进行集成，也就是在Service 调用 底层的XXXRepository extends Neo4jRepository ，
 * 可进行快速的增删 改查， 同时在Service集成了Neo4jVectorStore, 可以快速的进行添加向量存储 及检索。
 * 而且无需要自己考虑SearchRequest。
 */
public class DefaultNoteService implements NoteService{

    private final Notebook notebook;
    private final EmbeddingModel embeddingModel;
    private final NoteMapper noteMapper;
    private final Neo4jVectorStore vectorStore;

    public DefaultNoteService(Notebook notebook, EmbeddingModel embeddingModel, NoteMapper noteMapper, Neo4jVectorStore vectorStore){
        this.notebook = notebook;
        this.embeddingModel = embeddingModel;
        this.noteMapper = noteMapper;
        this.vectorStore = vectorStore;
    }

    @Override
    public void save(List<Note> notes) {
        notes.forEach(note -> note.notebook(notebook));
        List<Note> saved = noteMapper.saveAll(notes);
        // 必须拿保存后的笔记对象，才能拿到id
        var documents = saved.stream().map(note -> new Document(String.valueOf(note.id()),note.text(),
                Map.of("deviceId",notebook.deviceId(),"roleId",notebook.roleId()))).toList();
        vectorStore.add(documents);
    }

    @Override
    public List<Document> retrieve(String query, int topK) {
        Assert.hasText(query, "query can not be empty");
        topK = Math.max(topK, 1);
        topK = Math.min(topK, 10);
        FilterExpressionBuilder b = new FilterExpressionBuilder();
        Filter.Expression filter = b.and(b.eq("deviceId", notebook.deviceId()),b.eq("roleId", notebook.roleId())).build();
        SearchRequest request = SearchRequest.builder()
                .query(query)
                .filterExpression(filter)
                .topK(topK)
                .similarityThreshold(0.6f)
                .build();
        return vectorStore.similaritySearch(request);
    }

    @Override
    public void delete(Note note) {
        Assert.notNull(note.id(),"note id can not be null");
        noteMapper.delete(note);
    }

    @Override
    public void update(Note note) {
        Assert.notNull(note.id(),"note id can not be null");
        Note updated = noteMapper.save(note);
        var doc = new Document(String.valueOf(updated.id()),updated.text(),
                Map.of("deviceId",notebook.deviceId(),"roleId",notebook.roleId()));
        vectorStore.add(List.of(doc));
    }

    @Override
    public void save(Note note) {
        // 对于新增的笔记，需要将笔记和Notebook关联起来
        Assert.notNull(note.notebook(),"notebook can not be null");
        noteMapper.save(note);
        Document doc = new Document(String.valueOf(note.id()),note.text(),
                Map.of("deviceId",notebook.deviceId(),"roleId",notebook.roleId()));
        vectorStore.add(List.of(doc));
    }

}
